﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Media;


// socket example: simple chat
// - client can connect
// - client can send messages
// - client can disconnect 
// - thread for getting messages from server

namespace Client
{
    public partial class ClientForm : Form
    {
        #region MemberData
        
        private String m_sSvrIPAddress;
        private int m_nSvrPort = 9050;
        private IPEndPoint m_cMyAddress;
        private bool m_bConnected;
        private string m_sUserName;
        private const int MAX_MESSAGE_SIZE = 1000;
        private SoundPlayer Player = new SoundPlayer();
            

        #endregion MemberData



        public ClientForm()
        {
            InitializeComponent();

            // initalise
            m_cMyAddress = null;
            m_sUserName = "";

            // init display so can connect
            m_bConnected = false;
            setDisplay(m_bConnected);

            PlaySound();
            if (rBtn_local.Checked == true)
            {
                m_sSvrIPAddress = "127.0.0.1";
            }
            else if (rBtn_external.Checked == true)
            {
                tB_IP.Visible = true;
                tB_IP.Show();
                m_sSvrIPAddress = tB_IP.Text;
            }
        }

        #region ContactWithServer

        private void sendToServer(string sMessage, bool bResponse)
        {
            // connect to a specific end point
            // check actual with ipconfig (Eingabeaufforderung)
            IPEndPoint cSvrAddress = new IPEndPoint(IPAddress.Parse(m_sSvrIPAddress), m_nSvrPort);
            Socket cSvrSocket = new Socket(AddressFamily.InterNetwork,
                                         SocketType.Stream, ProtocolType.Tcp);
            
            // "dial"
            cSvrSocket.Connect(cSvrAddress);

            // remember the "number" I used to connect for receiving
            if (m_cMyAddress == null)
            {
                m_cMyAddress = (IPEndPoint)cSvrSocket.LocalEndPoint;
            }

            // send the message
            byte[] aDataBuffer = new byte[MAX_MESSAGE_SIZE];
            if (sMessage.Length > MAX_MESSAGE_SIZE)
            {
                sMessage = sMessage.Substring(0, MAX_MESSAGE_SIZE);
            }
            cSvrSocket.Send(Encoding.ASCII.GetBytes(sMessage));

            // receive response, if necessary
            if (bResponse)
            {
                int nAmountReceived;
                string sResponse = "";
                aDataBuffer = new byte[MAX_MESSAGE_SIZE];
                nAmountReceived = cSvrSocket.Receive(aDataBuffer);
                sResponse = Encoding.ASCII.GetString(aDataBuffer, 0, nAmountReceived);

                // connect returns name
                m_sUserName = sResponse;
            }

            // clean-up
            cSvrSocket.Shutdown(SocketShutdown.Both);
            cSvrSocket.Close();
        }

        // Thread that runs while connected
        private void listenForUpdates(Object stateInfo) // runs in thread
        {
            // start after connected
            // listen for updates until disconnected
            // will be closed when form closes (ThreadPool)

            // allow connection - only to specific address
            // server will send my messages to address passed in connect response (same one as used for connect)
            Socket cMyUpdateSocket = new Socket(AddressFamily.InterNetwork,
                                        SocketType.Stream, ProtocolType.Tcp);
            cMyUpdateSocket.Bind(m_cMyAddress);

            String sMessage;
            int nAmountReceived;
            byte[] aDataBuffer;

            // just keep listening while connected
            while (m_bConnected)
            {
                // Listen(Anzahl der Nachrichten, die in einer Warteschlange warten können, bis Sie abgearbeitet werden können
                cMyUpdateSocket.Listen(5); // smaller backlog ok
                
                // take connection from client
                Socket cSvrSocket = cMyUpdateSocket.Accept();

                // get the message
                aDataBuffer = new byte[MAX_MESSAGE_SIZE];
                nAmountReceived = cSvrSocket.Receive(aDataBuffer);
                if (nAmountReceived != 0)
                {
                    // get message text
                    sMessage = Encoding.ASCII.GetString(aDataBuffer, 0, nAmountReceived);

                    // display - GUI may be busy so queue 
                    Invoke(new UpdateUIDelegate(displayText), sMessage);

                    // no response
                }

                // clean-up from this exchange
                //cSvrSocket.Close();
                cSvrSocket.Disconnect(true); // allow it to be reused
            }

            // cleanup for cMyUpdateSocket never happens...
        }

        #endregion ContactWithServer

        #region GUIMethods

        private void setDisplay(bool bConnected)
        {
            if (bConnected == true)
            {
                this.BackColor = System.Drawing.Color.Orange;
                this.Height = 450;
                label4.Visible = false;
                rBtn_local.Visible = false;
                rBtn_external.Visible = false;
                tB_IP.Visible = false;
            }
            else
            {
                this.BackColor = System.Drawing.Color.SkyBlue;
                this.Height = 180;
            }
            // fields not displayed until connected
            label2.Visible = bConnected;
            tbDisplay.Visible = bConnected;
            label3.Visible = bConnected;
            tbMessage.Visible = bConnected;
            btnSend.Visible = bConnected;
            btnDisconnect.Visible = bConnected;
            
            // fields only needed until connected
            btnConnect.Visible = !bConnected;
            tbName.Enabled = !bConnected;
        }

        
        // called from listen thread when new message arrives
        public delegate void UpdateUIDelegate(string sText);
        private void displayText(string sText)
        {
            // add to display
            tbDisplay.Text += sText + "\r\n";
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            if (tbName.Text == "")
            {
                MessageBox.Show("Bitte geben Sie Ihren Nickname ein!");
                return;
            }

            // send connect
            try
            {
                sendToServer("connect " + tbName.Text, true);
                tbName.Text = m_sUserName; // should be set by response

                // display rest of interface
                m_bConnected = true;
                setDisplay(m_bConnected);

                // listen for updates
                ThreadPool.QueueUserWorkItem(listenForUpdates);
            }
            catch (SocketException ex)
            {
                MessageBox.Show("Client: Verbindung zum Server unmöglich: " + ex.Message);
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            // reset display - no reconnect possible
            m_bConnected = false;
            setDisplay(m_bConnected);
            this.Height = 100;
            tbName.Enabled = false;
            btnConnect.Enabled = false;

            // tell server
            try
            {
                sendToServer("disconnect " + m_sUserName, false);
            }
            catch (SocketException ex)
            {
                MessageBox.Show("Fehler beim Abmelden vom Server: " +ex.Message );
            }

            // let user know
            MessageBox.Show("Sie müssen das Programm neustarten um eine neue Verbindung aufzubauen. \n Das Programm zerstört sich in wenigen Sekunden von Selbst!");
            System.Threading.Thread.Sleep(4000);
            this.BackColor = System.Drawing.Color.Red;
            System.Threading.Thread.Sleep(2000);
            this.Close();
            // thread ends automatically when close or...
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            // only if something to send...
            if (tbMessage.Text == "")
            {
                return;
            }

            // connect to server
            try
            {
                sendToServer(m_sUserName + " " + tbMessage.Text, false);

                // empty once sent
                tbMessage.Text = "";
            }
            catch (SocketException ex)
            {
                MessageBox.Show("Client: nicht möglich die Nachricht dem Server zu senden: " + ex.Message);
                // hope better luck next time...
            }
        }

       private void PlaySound()
        {
            try
            {
                Player.SoundLocation = "TypingIM.wav";
                Player.Load();                
            }
            catch (Exception ex)
            {
                MessageBox.Show("Soundfehler: Die Sounddatei für die Tastaturgeräusche liegt nicht im Verzeichnis dieses Programms:\n\r " + ex.Message);
            }
        }
        
        private void activate_tBIP(object sender, EventArgs e)
        {
            tB_IP.Visible = true;
            tB_IP.Show();
        }

        private void tbMessage_TextChanged(object sender, EventArgs e)
        {
            Player.Play();
        }

        #endregion GUIMethods
    }
}